在 FROM 后面列出所有要用到的表,再在 WHERE 中写出“哪两列相等”这个连接条件。
SELECT 字段名
FROM 表1, 表2
WHERE 表1.共同列 = 表2.共同列;
class_id,所以可以按 class_id 把两张表连起来。
下面这两个表,才是数据库里真正存着的数据。连接查询不是“凭空查出来”的,而是把这两张表按共同列对上号。
| id | name | class_id |
|---|---|---|
| 1 | 张三 | 101 |
| 2 | 李四 | 102 |
| 3 | 王五 | 101 |
| class_id | class_name |
|---|---|
| 101 | 软件1班 |
| 102 | 大数据1班 |
class_id:101 对 101,102 对 102。所以张三和王五会连到“软件1班”,李四会连到“大数据1班”。
SELECT student.name, class.class_name
FROM student, class
WHERE student.class_id = class.class_id;
SELECT student.name, class.class_name
从结果里拿出两个字段:学生姓名、班级名称。
FROM student, class
让 student 表和 class 表一起参与查询。
WHERE student.class_id = class.class_id
按 class_id 相等来配对。也就是:学生表里的班级编号,要等于班级表里的班级编号。
可以顺着读成:
从 student 表和 class 表中,查询 student 表里的 name 字段和 class 表里的 class_name 字段,条件是 student.class_id 等于 class.class_id。
其中 WHERE 这句最关键,可以读成:“条件是学生表的 class_id 要等于班级表的 class_id。”
| name | class_name |
|---|---|
| 张三 | 软件1班 |
| 李四 | 大数据1班 |
| 王五 | 软件1班 |
用 JOIN 明确写出“和哪张表连接”,再用 ON 指定“按什么条件连接”。
SELECT 字段名
FROM 表1
INNER JOIN 表2 ON 表1.共同列 = 表2.共同列;
WHERE 里挪到了 ON 里,结果是一样的。
SELECT student.name, class.class_name
FROM student
INNER JOIN class ON student.class_id = class.class_id;
SELECT:还是取“姓名”和“班级名称”这两个字段。
FROM student INNER JOIN class
表示把 student 表和 class 表连接起来查。
ON student.class_id = class.class_id
表示连接条件是:两张表的 class_id 必须相等。
可以读成:
从 student 表中,把它和 class 表做内连接,连接条件是 student.class_id 等于 class.class_id,再取出 name 和 class_name 两个字段。
这里的 ON 可以直接理解成:“连接条件是……”
| name | class_name |
|---|---|
| 张三 | 软件1班 |
| 李四 | 大数据1班 |
| 王五 | 软件1班 |
ON:写“怎么连接”
student.class_id = class.class_id
WHERE:写“筛选谁”
class.class_name = '软件1班'
查询学号为"s5"的学生的选课信息,列出学号、姓名和课程号。
| sno | sn | maj |
|---|---|---|
| s1 | 李明 | 计算机 |
| s2 | 王彤 | 软件工程 |
| s5 | 赵敏 | 人工智能 |
| s6 | 刘洋 | 计算机 |
| sno | cno | score |
|---|---|---|
| s1 | c1 | 85 |
| s2 | c2 | 92 |
| s5 | c2 | 95 |
| s5 | c3 | 87 |
sno,所以可以通过 sno 把两张表连起来。SELECT s.sno, s.sn, sc.cno
FROM s, sc
WHERE s.sno = 's5' AND s.sno = sc.sno;
SELECT s.sno, s.sn, sc.cno:取出学生表里的学号和姓名,以及选课表里的课程号。
FROM s, sc:这次查询要同时看学生表和选课表。
WHERE s.sno = 's5':先锁定学号是 s5 的学生。
AND s.sno = sc.sno:再要求两张表里的学号相等,表示它们是同一个学生。
从 学生表 s 和 选课表 sc 里,
选择 学号、姓名、课程号,
条件是:学生学号等于 s5,
并且 s 表的学号等于 sc 表的学号。
SELECT s.sno, s.sn, sc.cno
FROM s INNER JOIN sc
ON s.sno = sc.sno
WHERE s.sno = 's5';
sno 相等来连;WHERE 负责说明"最终只看谁",这里是只看 s5。
| sno | sn | cno |
|---|---|---|
| s5 | 赵敏 | c2 |
| s5 | 赵敏 | c3 |
sno 字段。如果直接写 sno,数据库不知道你指的是哪张表的 sno,会报"二义性"错误。s.sno 或 sc.sno。
下面这句在很多数据库里可能不报错,因为 sn 只出现在学生表里,cno 只出现在选课表里,数据库还能猜到你要的是谁:
SELECT s.sno, sn, cno
FROM s, sc
WHERE s.sno = 's5' AND s.sno = sc.sno;
但教学时更推荐把字段写完整:
SELECT s.sno, s.sn, sc.cno
FROM s, sc
WHERE s.sno = 's5' AND s.sno = sc.sno;
原因很简单:更清楚、更统一、以后表一多也不容易出错。
在这个例子里,真正把学生表和选课表"连起来"的条件是哪一句?
SELECT s.sno, sn, cnoFROM s, scs.sno = sc.snos.sno = 's5's.sno = sc.sno。它表示两张表里的学号要相同,才能说明它们描述的是同一个学生。查询所有授课教师的教师号、姓名和课程名,按教师号升序排列。
这里涉及三张表:教师表 t → 授课表 tc → 课程表 c,通过链式连接把它们串起来。
| tno | tn |
|---|---|
| t1 | 李力 |
| t2 | 王平 |
| t3 | 陈琳 |
| t4 | 赵晴 |
| t5 | 刘波 |
| t6 | 张刚 |
| tno | cno |
|---|---|
| t1 | c2 |
| t2 | c3 |
| t3 | c1 |
| t4 | c2 |
| t5 | c4 |
| cno | cn | ct |
|---|---|---|
| c1 | 程序设计基础 | 48 |
| c2 | 数据库 | 64 |
| c3 | 数学 | 56 |
| c4 | 网页设计 | 32 |
t.tno = tc.tno,再用 tc.cno = c.cno。也就是先找到"谁教课",再找到"教的是哪门课"。SELECT t.tno, t.tn, c.cn
FROM t, tc, c
WHERE t.tno = tc.tno AND tc.cno = c.cno
ORDER BY t.tno;
SELECT t.tno, t.tn, c.cn
FROM t
INNER JOIN tc ON t.tno = tc.tno
INNER JOIN c ON tc.cno = c.cno
ORDER BY t.tno;
t 出发,用 t.tno = tc.tno 找到每位教师对应的授课记录;再用 tc.cno = c.cno 找到课程名称;最后按教师号升序排列。
| tno | tn | cn |
|---|---|---|
| t1 | 李力 | 数据库 |
| t2 | 王平 | 数学 |
| t3 | 陈琳 | 程序设计基础 |
| t4 | 赵晴 | 数据库 |
| t5 | 刘波 | 网页设计 |
在这个三表连接里,哪一列把授课表 tc 和课程表 c 连接起来?
t.tnotc.cno = c.cnotn = cnORDER BY tnotc 表里有课程号 cno,c 表里也有课程号 cno,所以这两张表就是通过 cno 连起来的。查询选课人数在 3 人及以上的课程号、课程名和选课人数。
| cno | cn |
|---|---|
| c1 | 程序设计基础 |
| c2 | 数据库 |
| c3 | 数学 |
| c4 | 网页设计 |
| sno | cno |
|---|---|
| s1 | c1 |
| s2 | c1 |
| s4 | c1 |
| s2 | c2 |
| s3 | c2 |
| s5 | c2 |
| s1 | c3 |
| s5 | c3 |
SELECT c.cno, c.cn, COUNT(sc.sno) AS 选课人数
FROM c INNER JOIN sc
ON c.cno = sc.cno
GROUP BY c.cno, c.cn
HAVING COUNT(sc.sno) >= 3;
① 先用 INNER JOIN 把课程表和选课表按 cno 连起来。
② 再用 GROUP BY 按课程号和课程名分组。
③ 然后用 COUNT 统计每门课被选了多少次。
④ 最后用 HAVING 只保留人数 ≥ 3 的课程。
从课程表和选课表中,
先按课程号把两张表连起来,
再按课程分组,统计每门课的选课人数,
最后只保留选课人数不少于 3 人的课程。
| cno | cn | 选课人数 |
|---|---|---|
| c1 | 程序设计基础 | 3 |
| c2 | 数据库 | 3 |
根据上面的原始数据,哪门课会被 HAVING COUNT(sc.sno) >= 3 过滤掉?
NULL 填充。
以 左边的表 为主表。左表所有行都保留,右表没匹配到的填 NULL。
以 右边的表 为主表。右表所有行都保留,左表没匹配到的填 NULL。
查询所有教师的教师号、姓名和授课程名(没有授课的教师也要显示)。
| tno | tn |
|---|---|
| t1 | 李力 |
| t2 | 王平 |
| t3 | 陈琳 |
| t4 | 赵晴 |
| t5 | 刘波 |
| t6 | 张刚 |
| tno | cno |
|---|---|
| t1 | c2 |
| t2 | c3 |
| t3 | c1 |
| t4 | c2 |
| t5 | c4 |
| cno | cn |
|---|---|
| c1 | 程序设计基础 |
| c2 | 数据库 |
| c3 | 数学 |
| c4 | 网页设计 |
LEFT JOIN。SELECT t.tno, t.tn, c.cn
FROM t LEFT OUTER JOIN tc ON t.tno = tc.tno
LEFT OUTER JOIN c ON tc.cno = c.cno
ORDER BY t.tno;
因为题目要求:没有授课的教师也要显示。
如果用 INNER JOIN,t6 张刚会被丢掉。
如果用 LEFT JOIN,左边教师表里的每个人都会保留。
右边没匹配到的课程信息,就显示为 NULL。
从教师表出发,
先把教师和授课表按教师号连起来,
再把授课表和课程表按课程号连起来,
即使没有授课记录,也保留该教师这一行。
与例 6-33 的内连接结果相比,本查询多出了张刚老师(t6),他没有授课记录,课程名显示为 NULL:
| tno | tn | cn |
|---|---|---|
| t1 | 李力 | 数据库 |
| t2 | 王平 | 数学 |
| t3 | 陈琳 | 程序设计基础 |
| t4 | 赵晴 | 数据库 |
| t5 | 刘波 | 网页设计 |
| t6 | 张刚 | NULL |
根据上面的原始表,哪位教师会因为 LEFT JOIN 而被额外保留下来?
NULL。查询所有学生的学号、姓名、课程号和成绩(没有选课的学生也要显示)。
| sno | sn |
|---|---|
| s1 | 李明 |
| s2 | 王彤 |
| s3 | 陈晨 |
| s4 | 周宇 |
| s5 | 赵敏 |
| s6 | 刘洋 |
| sno | cno | score |
|---|---|---|
| s1 | c1 | 85 |
| s2 | c2 | 92 |
| s3 | c2 | 78 |
| s5 | c3 | 87 |
SELECT s.sno, s.sn, sc.cno, sc.score
FROM s LEFT OUTER JOIN sc
ON s.sno = sc.sno;
学生表 s 在左边是主表 → 所有学生都保留,包括没有选课的 s6。
SELECT s.sno, s.sn, sc.cno, sc.score
FROM sc RIGHT OUTER JOIN s
ON s.sno = sc.sno;
把学生表放到右边,再用 RIGHT JOIN,本质上还是在保留学生表全部记录。
| sno | sn | cno | score |
|---|---|---|---|
| s1 | 李明 | c1 | 85 |
| s2 | 王彤 | c2 | 92 |
| s3 | 陈晨 | c2 | 78 |
| s4 | 周宇 | NULL | NULL |
| s5 | 赵敏 | c3 | 87 |
| s6 | 刘洋 | NULL | NULL |
| tno | tn |
|---|---|
| t1 | 李力 |
| t2 | 王平 |
| cno | cn |
|---|---|
| c1 | 程序设计基础 |
| c2 | 数据库 |
| c3 | 数学 |
SELECT *
FROM t CROSS JOIN c;
| tno | tn | cno | cn |
|---|---|---|---|
| t1 | 李力 | c1 | 程序设计基础 |
| t1 | 李力 | c2 | 数据库 |
| t1 | 李力 | c3 | 数学 |
| t2 | 王平 | c1 | 程序设计基础 |
| t2 | 王平 | c2 | 数据库 |
| t2 | 王平 | c3 | 数学 |
如果教师表有 5 行,课程表有 4 行,那么 CROSS JOIN 结果有多少行?
查询比"程序设计基础"课时高的课程号、课程名和课时。
| cno | cn | ct |
|---|---|---|
| c1 | 程序设计基础 | 48 |
| c2 | 数据库 | 64 |
| c3 | 数学 | 56 |
| c4 | 网页设计 | 32 |
-- 给课程表取两个别名:x 用来找结果,y 用来锁定"程序设计基础"
SELECT x.cno AS 课程号, x.cn AS 课程名, x.ct AS 课时
FROM c AS x INNER JOIN c AS y
ON x.ct > y.ct AND y.cn = '程序设计基础';
| 课程号 | 课程名 | 课时 |
|---|---|---|
| c2 | 数据库 | 64 |
| c3 | 数学 | 56 |
查询与学生"王彤"专业相同的学号和姓名。
| sno | sn | maj |
|---|---|---|
| s1 | 李明 | 计算机 |
| s2 | 王彤 | 软件工程 |
| s3 | 陈晨 | 数据科学 |
| s4 | 周宇 | 软件工程 |
| s5 | 赵敏 | 人工智能 |
SELECT x.sno, x.sn
FROM s AS x INNER JOIN s AS y
ON x.maj = y.maj AND y.sn = '王彤';
| sno | sn |
|---|---|
| s2 | 王彤 |
| s4 | 周宇 |
AND x.sn != '王彤'。
如果题目要求"查询与王彤专业相同、但不包含王彤本人",应该补哪一条条件?
AND y.sn != '王彤'AND x.sn != '王彤'AND x.maj != y.majAND x.sno = y.snoy 的任务是锁定王彤,所以不能把 y 排除掉。真正要从结果中去掉的是 x 这一侧的王彤,因此加 AND x.sn != '王彤'。1. 多表查询有两种写法:WHERE 连接和 JOIN 连接,推荐使用 JOIN 写法,语义更清晰。
2. INNER JOIN(内连接)只保留两表都匹配的行——"配不上就出局"。
3. LEFT/RIGHT JOIN(外连接)保留主表所有行,没匹配到的填 NULL——"即使没配上也要留名"。
4. CROSS JOIN(交叉连接)是全排列,结果行数 = 表1 × 表2,慎用。
5. 自连接是一张表和自己连接,用别名区分"两个分身",常用于同表内的比较。
6. 多表中存在同名字段时,必须加表名前缀(如 s.sno),否则会报二义性错误。
| sno | sn |
|---|---|
| s1 | 李明 |
| s2 | 王彤 |
| s6 | 刘洋 |
| sno | cno |
|---|---|
| s1 | c1 |
| s2 | c2 |
| tno | tn |
|---|---|
| t1 | 李力 |
| t6 | 张刚 |
如果要查询所有学生的学号、姓名及其选课课程号,并且要求没有选课的学生也显示,应该用哪种连接?
如果执行 SELECT * FROM s CROSS JOIN sc;,上面这组小数据会得到多少行?(学生表 s 有 3 行,选课表 sc 有 2 行)
如果要查询所有教师及其课程名,并且没有授课的教师也显示,例如 t6 张刚,那么哪种说法正确?